﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using PhotoViewerLib;

namespace PhotoViewerDesktop
{
    public partial class MainForm : Form
    {
        //A list that contains all the various categories
        private List<Category> categories;
        //A list that contains all the photos of the user
        private List<Photo> photos;

        //The picture currently displayed in the main panel
        private Photo displayedPhoto;

        //The max size for the thumbnail
        private const int ThumbSize = 80;
        //The max size for the main PictureBox
        private const int MainPictureSize = 400;

        //A list that contains the tags added to a picture in order to save them
        private List<Tag> newTags;

        public MainForm()
        {
            InitializeComponent();
        }

        private void MainForm_Load(object sender, EventArgs e)
        {
            //We initiate the various datastructures & components
            categories = Util.getListCategory();
            photos = Util.getListPhotos();

            newTags = new List<Tag>();

            CBoxTypeSearch_Load();

            TBarRating.MouseUp += new MouseEventHandler(TBarRating_MouseUp);

            //We fill the listbox with all the categories' name
            categLB.Items.Add("All");
            foreach (Category c in categories)
            {
                categLB.Items.Add(c.name);
            }
            categLB.SetSelected(0, true);
        }

        //This function creates a new photo from a file path.
        //This photo is automatically created in the selected category
        private void AddPhoto(String filePath)
        {
            Photo newPhoto = new Photo(filePath);
            foreach (Category c in categories)
            {
                if (c.name == ((String)((ListBox)categLB).SelectedItem))
                {
                    newPhoto.cid = c.uid;
                    c.photos.Add(newPhoto);
                    break;
                }
            }
            Util.addPhoto(newPhoto);
            photos = Util.getListPhotos();
        }

        //This function deletes a picture
        private void pictureDelete()
        {
            Photo toremove = null;
            foreach (Photo p in photos)
            {
                if (p.filePath == (String)((PictureBox)LblDeletePhoto.Tag).Tag)
                {
                    toremove = p;
                    break;
                }
            }
            Util.delPhoto(toremove.uid);
            photos.Remove(toremove);
            PanelPhotos.Controls.Remove((Control)LblDeletePhoto.Tag);
        }

        //This function displays a photo in the main PictureBox.
        //In addition of the picture, various metadata are displayed
        //Also, some actions are available.
        private void displayPhoto(object sender, EventArgs e)
        {
            foreach (Photo p in photos)
            {
                //The photo is retrieved from its filepath, which is kept
                //in the PictureBox tag field
                if (p.filePath == (String)((PictureBox)sender).Tag)
                {
                    displayedPhoto = p;
                    fillInExif(p);
                    PbMainPhoto.Image = p.image;
                    resizePictureBox(MainPictureSize, PbMainPhoto);
                    ToolTipPhotoPath.SetToolTip(PbMainPhoto, p.filePath);
                    ToolTipPhotoPath.Active = true;
                    LblPhotoDate.Text = p.date.Day + "/" + p.date.Month + "/" + p.date.Year;
                    LblPhotoWidth.Text = p.width.ToString() + " pixels";
                    LblPhotoHeight.Text = p.height.ToString() + " pixels";
                    LblPhotoType.Text = p.fileType;
                    foreach (Category c in categories)
                    {
                        if (c.uid == p.cid)
                        {
                            LblPhotoCateg.Text = c.name;
                            break;
                        }
                        LblPhotoCateg.Text = "None";
                    }
                    LblPhotoRating.Text = "";
                    if (displayedPhoto.rating <= 0)
                        LblPhotoRating.Text = "Not rated yet";
                    for (int i = 1; i <= p.rating; ++i)
                    {
                        LblPhotoRating.Text += "* ";
                    }
                    LblPhotoTags.Text = "";
                    foreach (Tag t in p.tags)
                    {
                        LblPhotoTags.Text += t.title + "; ";
                    }
                    BoxPhotoMetadata.Visible = true;

                    BoxPhotoActions.Visible = true ;
                    LblDeletePhoto.Tag = sender;
                    break;
                }
            }
        }

        //This function fills the panel for the EXIF metadata.
        private void fillInExif(Photo p)
        {
            exifPropPanel.Controls.Clear();
            foreach (KeyValuePair<string, string> kvp in p.exifProperties)
            {
                Label tmpLbl = new Label();
                tmpLbl.Text = kvp.Key + " : " + kvp.Value;
                exifPropPanel.Controls.Add(tmpLbl);
            }
        }

        //This function resizez the various PictureBox in order not to deform the picture
        //The parameters are : the PictureBox, and the maximum size of it
        private void resizePictureBox(int maxSize, PictureBox pb)
        {
            pb.Width = maxSize;
            pb.Height = maxSize;
            pb.MaximumSize = new Size(maxSize, maxSize);
            //We check which of the width or the height is greater and we set it at maxSize
            if (pb.Image.Width > pb.Width || pb.Image.Height > pb.Width)
            {
                pb.SizeMode = PictureBoxSizeMode.StretchImage;
                //Then we set the smaller dimension at the proportional value of the picture
                if (pb.Image.Width >= pb.Image.Height)
                {
                    pb.Height = (int)(pb.Image.Height * pb.Width / pb.Image.Width);
                }
                else
                {
                    pb.Width = (int)(pb.Image.Width * pb.Height / pb.Image.Height);
                }
            }
            //If the picture is smaller than the max size, nothing is done, 
            //and the picture is normally displayed
            else
            {
                pb.SizeMode = PictureBoxSizeMode.Normal;
            }
        }

        //This function adds a category to the categories List and to the database
        //Then the ListBox is refreshed
        public void AddCategory(String newCategName)
        {
            Util.addCategory(new Category(newCategName));
            categories = Util.getListCategory();
            this.redrawCategLB();
        }

        //This function refreshed the ListBox of categories and set the selected one to "All"
        private void redrawCategLB()
        {
            categLB.Items.Clear();
            categLB.Items.Add("All");
            foreach (Category c in categories)
            {
                categLB.Items.Add(c.name);
            }
            categLB.SetSelected(0, true);
        }

        //This function displays all the photos within a category when selected in the ListBox
        private void categLB_SelectedIndexChanged(object sender, EventArgs e)
        {
            PanelPhotos.Controls.Clear();
            PbMainPhoto.Image = null;
            BoxPhotoMetadata.Visible = false;
            BoxPhotoActions.Visible = false;
            ToolTipPhotoPath.Active = false;
            exifPropPanel.Controls.Clear();

            if ((String)((ListBox)sender).SelectedItem != "All")
            {
                Category selectedCateg = null;
                foreach (Category c in categories)
                {
                    if (c.name == (String)((ListBox)sender).SelectedItem)
                    {
                        selectedCateg = c;
                        break;
                    }
                }
                foreach (Photo p in photos)
                {
                    if (p.cid == selectedCateg.uid && p.image != null)
                    {
                        PictureBox pbTmp = new PictureBox();
                        pbTmp.Image = p.image;
                        resizePictureBox(ThumbSize, pbTmp);
                        pbTmp.Tag = p.filePath;
                        pbTmp.Click += new EventHandler(displayPhoto);
                        PanelPhotos.Controls.Add(pbTmp);
                    }
                }
            }
            //If the selected category is "All", we do not filter the category id
            //and all the pictures are displayed
            else
            {
                foreach (Photo p in photos)
                {
                    if (p.image != null)
                    {
                        PictureBox pbTmp = new PictureBox();
                        pbTmp.Image = p.image;
                        resizePictureBox(ThumbSize, pbTmp);
                        pbTmp.Tag = p.filePath;
                        pbTmp.Click += new EventHandler(displayPhoto);
                        PanelPhotos.Controls.Add(pbTmp);
                    }
                }
            }
        }

        //This function opens the form to add a new category
        private void newCategoryToolStripMenuItem_Click(object sender, EventArgs e)
        {
            new InputBoxCateg(this).Show();
        }

        //This function allows to import a photo from the filesystem
        //The thumbnail of the picture is added to the current category photo panel
        private void addPhotoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (FileExplorer.ShowDialog() == DialogResult.OK)
            {
                AddPhoto(FileExplorer.FileName);
                PictureBox pbTmp = new PictureBox();
                pbTmp.Image = Image.FromFile(FileExplorer.FileName);
                resizePictureBox(ThumbSize, pbTmp);
                pbTmp.SizeMode = PictureBoxSizeMode.StretchImage;
                pbTmp.Click += new EventHandler(displayPhoto);
                pbTmp.Tag = FileExplorer.FileName;
                PanelPhotos.Controls.Add(pbTmp);
            }
        }

        //This function adds a tag to a photo
        private void BtnTagPhoto_Click(object sender, EventArgs e)
        {
            if (displayedPhoto != null)
            {
                Tag newTag = new Tag(TxtBoxTag.Text);
                newTag.pid = displayedPhoto.uid;
                displayedPhoto.tags.Add(newTag);
                newTags.Add(newTag);
                LblPhotoTags.Text += TxtBoxTag.Text + "; ";
                TxtBoxTag.Clear();
            }
        }

        //This function activates the tag mode, displaying the controls to tag a photo
        private void LblAddTag_Click(object sender, EventArgs e)
        {
            LblAddTag.Visible = false;
            TxtBoxTag.Visible = true;
            BtnTagPhoto.Visible = true;
            BtnEndTaging.Visible = true;
        }

        //This function turns off the tag mode, hiding the controls.
        private void BtnEndTaging_Click(object sender, EventArgs e)
        {
            LblAddTag.Visible = true;
            TxtBoxTag.Clear();
            TxtBoxTag.Visible = false;
            BtnTagPhoto.Visible = false;
            BtnEndTaging.Visible = false;

            //If a tag had been added to the newTags list, the list is saved in the database
            if (newTags.Count > 0)
            {
                Util.addTag(newTags);
                newTags.Clear();
            }
        }

        //This functon activates the rating mode
        private void LblPhotoRating_Click(object sender, EventArgs e)
        {
            TBarRating.Visible = true;
            LblPhotoRating.Visible = false;
            //The trackbar's value is set to the current photo rating
            //or to 1 if the poto has not been rated already
            if (displayedPhoto.rating > 0)
                TBarRating.Value = displayedPhoto.rating;
            else
                TBarRating.Value = TBarRating.Minimum ;
        }

        //This function is called when the user releases the trackbar
        //It saves the new rate in the photo list andthe database
        //and quits the rating mode
        private void TBarRating_MouseUp(object sender, EventArgs e)
        {
            displayedPhoto.rating = (int)TBarRating.Value;
            LblPhotoRating.Text = "";
            for (int i = 1; i <= displayedPhoto.rating; ++i)
            {
                LblPhotoRating.Text += "* ";
            }
            TBarRating.Visible = false;
            LblPhotoRating.Visible = true;
            Util.editPhoto(displayedPhoto.uid, displayedPhoto);
        }

        //This function activates the change category mode
        private void LblChangeCateg_Click(object sender, EventArgs e)
        {
            CBChangeCateg.Items.Clear();
            foreach (Category c in categories)
            {
                CBChangeCateg.Items.Add(c.name);
            }

            LblChangeCateg.Visible = false;
            CBChangeCateg.Visible = true;
            BtnChangeCateg.Visible = true;
        }

        //This function modifies the category of a photo
        private void BtnChangeCateg_Click(object sender, EventArgs e)
        {
            foreach (Category c in categories)
            {
                if (c.name == CBChangeCateg.Text)
                {
                    displayedPhoto.cid = c.uid;
                    c.photos.Add(displayedPhoto);
                    foreach (String s in categLB.Items)
                    {
                        if (s == c.name)
                        {
                            categLB.SelectedItem = s;
                            break;
                        }
                    }
                    //The change is saved in the database, for the photo and for the category
                    Util.editPhoto(displayedPhoto.uid, displayedPhoto);
                    Util.editCategory(c.uid, c);
                    break;
                }
            }
            //The change category mode is quit
            LblChangeCateg.Visible = true;
            CBChangeCateg.Visible = false;
            BtnChangeCateg.Visible = false;
        }

        //This function deletes a function from the graphical application (after confirmation)
        private void LblDeletePhoto_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Are you sure?", "Delete Photo", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                pictureDelete();
                PbMainPhoto.Image = null;
                ToolTipPhotoPath.Active = false;
                BoxPhotoMetadata.Visible = false;
                ToolTipPhotoPath.Active = false;
                BoxPhotoActions.Visible = false;
                exifPropPanel.Controls.Clear();
            }
        }

        //This function displays the current picture in its full size
        private void LblFullSize_Click(object sender, EventArgs e)
        {
            new FullSizePhoto(displayedPhoto.image).Show();
        }

        //This function creates a backup of the database
        private void backupDatabaseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (BackupFolderBrowser.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    System.IO.File.Copy(Util.getDatabaseFilePath(), System.IO.Path.Combine(BackupFolderBrowser.SelectedPath, "backup.s3db"));
                }
                catch (System.IO.IOException ex)
                {
                    System.IO.File.Replace(Util.getDatabaseFilePath(), System.IO.Path.Combine(BackupFolderBrowser.SelectedPath, "backup.s3db"),
                    System.IO.Path.Combine(System.IO.Path.GetFullPath(Util.getDatabaseFilePath()), "backupDB.s3db"));
                }
            }
        }

        //This function imports another database
        private void restoreDatabaseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (FileExplorer.ShowDialog() == DialogResult.OK)
            {   
                String oldDbPath = Util.getDatabaseFilePath();
                //The data of the new database are retrieved
                Util.setDbFilePath(FileExplorer.FileName);
                categories = Util.getListCategory();
                photos = Util.getListPhotos();

                Util.setDbFilePath(oldDbPath);
                //Then the old database is cleared and the new data are saved
                Util.clearDB();
                foreach (Category c in categories)
                {
                    Util.addCategory(c);
                }
                foreach (Photo p in photos)
                {
                    Util.addPhoto(p);
                }
                //Finally the data in the lists are updated, and the application is refreshed
                categories = Util.getListCategory();
                photos = Util.getListPhotos();
                redrawCategLB();
            }
        }

        //This function displays the current photo in its full size
        private void PbMainPhoto_Click(object sender, EventArgs e)
        {
            LblFullSize_Click(sender, e);
        }

        //This function deletes a category from the system (after confirmation)
        private void delCategoryToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show("Are you sure?", "Delete category", MessageBoxButtons.YesNo) == DialogResult.Yes)
            {
                foreach (Category c in categories)
                {
                    if (c.name == categLB.SelectedItem.ToString())
                    {
                        foreach (Photo p in photos)
                        {
                            if (p.cid == c.uid)
                                p.cid = -1;
                        }
                        break;
                    }
                    redrawCategLB();
                }
                
            }
        }

        //This function initiates the ComboBox for the search
        private void CBoxTypeSearch_Load()
        {
            CBoxTypeSearch.Items.Add("All");
            CBoxTypeSearch.Items.Add("Name");
            CBoxTypeSearch.Items.Add("Tag");
            CBoxTypeSearch.Items.Add("Rate");
            CBoxTypeSearch.Items.Add("Date");
        }

        //This function displays different controls depending on the type of research selected.
        //If none is, the function displays all the photos normally
        private void CBoxTypeSearch_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch (CBoxTypeSearch.SelectedIndex)
            {
                
                case 3:
                    TBoxSearch.Visible = false;
                    TBarRatingSearch.Visible = true;
                    DatePickEnd.Visible = false;
                    DatePickStart.Visible = false;
                    break;
                case 4:
                    TBoxSearch.Visible = false;
                    TBarRatingSearch.Visible = false;
                    DatePickEnd.Visible = true;
                    DatePickStart.Visible = true;
                    break;
                case 0:
                    photos = Util.getListPhotos();
                    redrawCategLB();
                    TBoxSearch.Visible = true;
                    TBoxSearch.Text = "";
                    TBarRatingSearch.Visible = false;
                    DatePickEnd.Visible = false;
                    DatePickStart.Visible = false;
                    break;
                default:
                    TBoxSearch.Visible = true;
                    TBarRatingSearch.Visible = false;
                    DatePickEnd.Visible = false;
                    DatePickStart.Visible = false;
                    break;
            }
        }

        //This function filters the photos depending on the parameter for the research
        private void BtnSearch_Click(object sender, EventArgs e)
        {
                List<Photo> filteredPhotos = new List<Photo>();
                switch (CBoxTypeSearch.SelectedIndex)
                {
                    case 0:
                        foreach (Photo p in photos)
                        {
                            if (p.filePath.Contains(TBoxSearch.Text))
                                filteredPhotos.Add(p);
                            foreach (Tag t in p.tags)
                            {
                                if (t.title == TBoxSearch.Text)
                                {
                                    filteredPhotos.Add(p);
                                    break;
                                }
                            }
                        }
                        break;
                    case 1:
                        foreach (Photo p in photos)
                        {
                            if (p.filePath.Contains(TBoxSearch.Text))
                                filteredPhotos.Add(p);
                        }
                        break;
                    case 2:
                        foreach (Photo p in photos)
                        {
                            foreach (Tag t in p.tags)
                            {
                                if (t.title == TBoxSearch.Text)
                                {
                                    filteredPhotos.Add(p);
                                    break;
                                }
                            }
                        }
                        break;
                    case 3:
                        foreach (Photo p in photos)
                        {
                            if (p.rating == TBarRatingSearch.Value)
                                filteredPhotos.Add(p);
                        }
                        break;
                    case 4:
                        foreach (Photo p in photos)
                        {
                            if (p.date.CompareTo(DatePickStart) >= 0 && p.date.CompareTo(DatePickEnd) <=0)
                                filteredPhotos.Add(p);
                        }
                        break;
                    default:
                        break;
                }
                photos = filteredPhotos;
                redrawCategLB();
        }

        //This function clears the search filter
        private void clearSearchBtn_Click(object sender, EventArgs e)
        {
            photos = Util.getListPhotos();
            categLB.SetSelected(0, true);
        }

    }
}
